﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;
using System.IO;

namespace GJQT
{
    public class Repository
    {
        public static string GetInstallationPath()
        {
            try
            {
                using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\GuJian"))
                {
                    return (string)key.GetValue("default");
                }
            }
            catch (Exception)
            {
                return null;
            }
        }
    }

#if false
    interface GJResourceObject
    {
        string Name { get; }
    }

    /// <summary>
    /// Represents a resource item that can be loaded as a binary stream.
    /// </summary>
    interface GJResourceItem : GJResourceObject
    {
        int Size { get; }
        void GetData(byte[] data, int startIndex, int size);
    }

    /// <summary>
    /// Represents a collection of resource items and folders.
    /// </summary>
    interface GJResourceFolder : GJResourceObject
    {
        bool HasSubFolders { get; }
        GJResourceItem[] Items { get; }
        GJResourceFolder[] Folders { get; }
    }

    /// <summary>
    /// Implements a GJResourceItem on back of an entry in a ZpkArchive.
    /// </summary>
    class GJZpkVirtualFile : GJResourceItem
    {
        private ZpkEntry m_entry;

        public GJZpkVirtualFile(ZpkEntry entry)
        {
            m_entry = entry;
        }

        public string Name
        {
            get { return Path.GetFileName(m_entry.Name); }
        }

        public int Size
        {
            get { return (int)m_entry.Size; }
        }

        public void GetData(byte[] data, int startIndex, int size)
        {
            throw new NotImplementedException();
        }

        public ZpkEntry Entry
        {
            get { return m_entry; }
        }
    }

    /// <summary>
    /// Implements a GJResourceFolder on back of a virtual folder in a ZpkArchive.
    /// </summary>
    class GJZpkVirtualFolder : GJResourceFolder
    {
        private ZpkArchive m_ar;    // The ZPK archive containing this virtual folder.
        private string m_path;      // The path of the virtual folder in the archive,
        // e.g. background/combat
        // If this is the empty string, then it represents
        // the root folder in a ZPK archive.
        private GJZpkVirtualFile[] m_files;         // cache items
        private GJZpkVirtualFolder[] m_folders;     // cache items

        public GJZpkVirtualFolder()
        {
            m_ar = null;
            m_path = "";
            m_files = new GJZpkVirtualFile[0];
            m_folders = new GJZpkVirtualFolder[0];
        }

        public GJZpkVirtualFolder(ZpkArchive ar, string path)
        {
            m_ar = ar;
            m_path = path;
            m_files = null;
            m_folders = null;
        }

        private void LazyLoad()
        {
            if (m_files != null || m_folders != null)
                return;

            Dictionary<string, bool> folderMap = new Dictionary<string, bool>();
            List<GJZpkVirtualFolder> folders = new List<GJZpkVirtualFolder>();
            List<GJZpkVirtualFile> files = new List<GJZpkVirtualFile>();

            for (int i = 0; i < m_ar.EntryCount; i++) {
                ZpkEntry ent = m_ar.GetEntry(i);
                string s = m_ar.GetEntry(i).Name;
                if (m_path != "") {
                    if (!s.StartsWith(m_path + '/', StringComparison.InvariantCultureIgnoreCase))
                        continue;
                    s = s.Substring(m_path.Length + 1);
                }
                int k = s.IndexOf('/');
                if (k >= 0) { // is folder
                    s = s.Substring(0, k);
                    if (!folderMap.ContainsKey(s.ToLowerInvariant())) {
                        GJZpkVirtualFolder folder = new GJZpkVirtualFolder(m_ar,
                    (m_path == "") ? s : m_path + '/' + s);
                        folders.Add(folder);
                        folderMap.Add(s.ToLowerInvariant(), true);
                    }
                } else { // is file
                    files.Add(new GJZpkVirtualFile(ent));
                }
            }

            m_files = files.ToArray();
            m_folders = folders.ToArray();
        }

        public string Name
        {
            get { return Path.GetFileName(m_path); }
        }

        public GJResourceItem[] Items
        {
            get
            {
                LazyLoad();
                return m_files;
            }
        }

        public GJResourceFolder[] Folders
        {
            get
            {
                LazyLoad();
                return m_folders;
            }
        }

        public bool HasSubFolders
        {
            get { return (this.Folders.Length > 0); }
        }
    }

    /// <summary>
    /// Implements a GJResourceFolder on back of a ZpkArchive.
    /// </summary>
    class GJZpkArchive : GJResourceFolder
    {
        private string m_filename;  // filename 
        private ZpkArchive m_ar;
        private GJZpkVirtualFolder m_rootFolder;

        public GJZpkArchive(string filename)
        {
            m_filename = filename;
            m_ar = null;
            m_rootFolder = null;
        }

        private void LazyLoad()
        {
            if (m_rootFolder != null)
                return;

            try {
                m_ar = new ZpkArchive(m_filename, true);
                m_rootFolder = new GJZpkVirtualFolder(m_ar, "");
            } catch (ZpkException) {
                m_ar = null;
                m_rootFolder = new GJZpkVirtualFolder();
            }
        }

        public ZpkArchive Archive
        {
            get
            {
                LazyLoad();
                return m_ar;
            }
        }

        public string Name
        {
            get { return Path.GetFileNameWithoutExtension(m_filename); }
        }

        public GJResourceItem[] Items
        {
            get
            {
                LazyLoad();
                return m_rootFolder.Items;
            }
        }

        public GJResourceFolder[] Folders
        {
            get
            {
                LazyLoad();
                return m_rootFolder.Folders;
            }
        }

        public bool HasSubFolders
        {
            get { return (m_rootFolder == null) ? true : m_rootFolder.HasSubFolders; }
        }
    }

    /// <summary>
    /// Implements a GJResourceFolder on back of a directory containing ZPK archives.
    /// </summary>
    class GJZpkDirectory : GJResourceFolder
    {
        private string m_path;
        private GJZpkDirectory[] m_directories;
        private GJZpkArchive[] m_archives;
        private GJResourceFolder[] m_folders;

        public GJZpkDirectory(string path)
        {
            m_path = path;
            m_directories = null;
            m_archives = null;
            m_folders = null;
        }

        private void LazyLoad()
        {
            if (m_folders != null)
                return;

            DirectoryInfo dir = new DirectoryInfo(m_path);

            // List ZPK archives in this directory.
            List<GJZpkArchive> archives = new List<GJZpkArchive>();
            FileInfo[] fis = null;
            try {
                fis = dir.GetFiles("*.zpk");
            } catch (Exception) { 
                // All kinds of exceptions may occur, e.g. NoAccessException if
                // the folder we examine happens to be "SystemVolumeInformation"!
            }
            if (fis != null) {
                foreach (FileInfo fi in fis) {
                    archives.Add(new GJZpkArchive(fi.FullName));
                }
            }
            m_archives = archives.ToArray();

            // List sub-directories in this directory.
            List<GJZpkDirectory> directories = new List<GJZpkDirectory>();
            DirectoryInfo[] dis = null;
            try {
                dis = dir.GetDirectories();
            } catch (Exception) {
            }
            if (dis != null) {
                foreach (DirectoryInfo di in dis) {
                    GJZpkDirectory subdir = new GJZpkDirectory(di.FullName);
                    // We remove the recursive checking below in case we're at
                    // the top of a very deep directory tree (such as C:\).
                    //if (subdir.Folders.Length > 0) {
                        directories.Add(subdir);
                    //}
                }
            }
            m_directories = directories.ToArray();

            // Combine archives and sub-directories as virtual folders.
            m_folders = new GJResourceFolder[m_directories.Length + m_archives.Length];
            m_directories.CopyTo(m_folders, 0);
            m_archives.CopyTo(m_folders, m_directories.Length);
        }

        public string Name
        {
            get { return Path.GetFileName(m_path); }
        }

        public GJResourceItem[] Items
        {
            get { return new GJResourceItem[0]; }
        }

        public GJResourceFolder[] Folders
        {
            get
            {
                LazyLoad();
                return m_folders;
            }
        }

        public bool HasSubFolders
        {
            get { return (this.Folders.Length > 0); }
        }
    }

    class GJRepository : GJResourceFolder
    {
        public static string DefaultGamePath
        {
            get
            {
                try {
                    RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Software\GuJian");
                    return Path.Combine((string)rk.GetValue("default"), "");
                } catch (Exception) {
                    return null;
                }
            }
        }

        public static string DefaultLocation
        {
            get
            {
                try {
                    RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Software\GuJian");
                    return Path.Combine((string)rk.GetValue("default"), "Data");
                } catch (Exception) {
                    return null;
                }
            }
        }

        private GJZpkDirectory m_rootDirectory;

        public GJRepository(string location)
        {
            m_rootDirectory = new GJZpkDirectory(location);
        }

        public string Name
        {
            get { return "古剑资源"; }
        }

        public GJResourceItem[] Items
        {
            get { return m_rootDirectory.Items; }
        }

        public GJResourceFolder[] Folders
        {
            get { return m_rootDirectory.Folders; }
        }

        public bool HasSubFolders
        {
            get { return m_rootDirectory.HasSubFolders; }
        }
    }

#if false
        private static void FindZpkArchives(List<string> results, string path, bool recursive)
        {
            Regex rgx = new Regex(@"^(.+)[.]zpk([.](\d+))?$", RegexOptions.IgnoreCase);
            DirectoryInfo dir = new DirectoryInfo(path);
            foreach (FileInfo fi in dir.GetFiles()) {
                string name = fi.Name;
                Match m = rgx.Match(name);
                if (m.Success) {
                    string basename = m.Groups[1].Captures[0].Value;
                    int version = (m.Groups[3].Captures.Count > 0) ?
                        Int32.Parse(m.Groups[3].Captures[0].Value) : -1;
                    results.Add(fi.FullName);
                }
            }
            if (recursive) {
                foreach (DirectoryInfo di in dir.GetDirectories()) {
                    FindZpkArchives(results, di.FullName, recursive);
                }
            }
        }

        private static string[] FindZpkArchives()
        {
            string path = System.IO.Path.Combine(GetGJPath(), "Data");
            List<string> results = new List<string>();
            FindZpkArchives(results, path, true);
            return results.ToArray();
        }
#endif
#endif
}
